using System;
using System.Collections.Generic;
using System.Text;
using System.Web;
using NHibernate;
using NHibernate.Criterion;
using NHibernate.Type;
using App.Modelo.Interfaces;

namespace App.Persistencia
{
    public class Repositorio : IRepositorio, IDisposable
    {

        public static event EventHandler<EventArgs> SessionCreada;

        public event EventHandler<TriggerEventHandler> FlushData;

        private static Repositorio _Repositorio;
        private FabricaSesiones _FabricaSesiones;
        private ISession _activeSession;

        public Repositorio()
        {
            _FabricaSesiones = FabricaSesiones.Instance;
            _activeSession = _FabricaSesiones.GetSession(new Interceptor(this._notify));
            
            _activeSession.EnableFilter("avoidLogicalDeleted");

            if( null != SessionCreada)
                SessionCreada(this, new EventArgs());
        }

        public Repositorio(ISession Session )
        {
            _activeSession = Session;
        }

        public ISessionFactory SessionFactory
        {
            get
            {
                return FabricaSesiones.Instance.GetSessionFactory();
            }
        }
        public static Repositorio Current
        {
            get
            {
                if (_Repositorio == null)
                    _Repositorio = new Repositorio();
                return _Repositorio;
            }
        }

        private void _notify(
            TriggerActionType action,
            object entity,
            object id,
            object[] currentState,
            object[] previousState,
            string[] propertyNames,
            IType[] types)
        {
            if (FlushData != null)
                FlushData(this, new TriggerEventHandler()
                {
                    Action = action,
                    CurrentState = currentState,
                    Id = id,
                    PreviousState = previousState,
                    PropertyNames = propertyNames,
                    Entity = entity
                });
                
        }


        public void Refresh(object obj)
        {
            _activeSession.Refresh(obj);
        }

        #region Session Methods


        public object GetSessionId()
        {
            var c = _activeSession.Connection.CreateCommand();
            c.CommandText = "SELECT @@SPID AS session_id";
            return c.ExecuteScalar();
        }

        public System.Data.IDbConnection Connection
        {
            get
            {
                return _activeSession != null ? _activeSession.Connection :null;
            }
        }

        public void OpenSession()
        {
            if(_activeSession != null && _activeSession.IsOpen)
                return;
            
            if (_activeSession == null || ! _activeSession.IsOpen)
                _activeSession = _FabricaSesiones.GetSession(new Interceptor(this._notify));
            else if (null == _activeSession)
                throw new ApplicationException("The repository already has an open session");
        }

        public void FlushSession()
        {
            if (_activeSession != null && _activeSession.IsOpen)
                _activeSession.Flush();
        }

        public void CloseSession()
        {
            if (_activeSession != null)
            {
                if (_activeSession.IsOpen)
                    _activeSession.Close();
                

                _activeSession.Dispose();
            }
        }

        public ISession Session
        {
            get
            {
                if (_activeSession == null || (_activeSession != null && !_activeSession.IsOpen))
                    this.OpenSession();

                return _activeSession;
            }
        }

        public void BeginTransaccion()
        {
            _activeSession.Transaction.Begin();
        }

        public void CommitTransaction()
        {
            _activeSession.Transaction.Commit();
        }

        public void RoolbackTransaction()
        {
            _activeSession.Transaction.Rollback();
        }
        #endregion

        

        #region Object Methods

        public void SaveObject(object obj)
        {
            _activeSession.Save(obj);
            this.FlushSession();
        }

        public void UpdateObject(object obj)
        {
            _activeSession.Update(obj);
        }

        public void DeleteObject(object obj)
        {
            _activeSession.Delete(obj);
            _activeSession.Flush();
        }


        #endregion


        #region Metodos Extendidos

        #region GetById
        public T GetById<T>(object id)
        {
            return (T)_activeSession.Get(typeof(T), id);
        }
        #endregion

        #region GetList
        public IList<T> GetList<T>(string _hql)
        {
            return _activeSession.CreateQuery(_hql).List<T>();
        }

        public IList<T> GetList<T>(string _hql, IDictionary<string, object> _param)
        {
            IQuery query = _activeSession.CreateQuery(_hql);
            
            foreach (KeyValuePair<string, object> oitem in _param)
            {
                if (oitem.Value == null)
                {
                    query.SetParameter(Convert.ToString(oitem.Key), null, NHibernateUtil.Int32);
                }
                else
                {
                    query.SetParameter(Convert.ToString(oitem.Key), oitem.Value);
                }
            }

            return query.List<T>();
        }

        public IList<T> GetList<T>(string _hql, IDictionary<string, string> _param)
        {
            IQuery query = _activeSession.CreateQuery(_hql);

            foreach (KeyValuePair<string, string> oitem in _param)
            {
                if (oitem.Value == null)
                {
                    query.SetParameter(oitem.Key, null, NHibernateUtil.Int32);
                }
                else
                {
                    query.SetParameter(oitem.Key, oitem.Value);
                }
            }

            return query.List<T>();
        }


        public IList<T> GetList<T>(string filterPropertyName, string filterPropertyValue)
        {
            return _activeSession
                .CreateCriteria(typeof(T))
                .Add( Restrictions.Eq(filterPropertyName, filterPropertyValue))
                .List<T>();
        }

        public IList<T> GetList<T>(string filterPropertyName, object filterPropertyValue)
        {
            return _activeSession
                .CreateCriteria(typeof(T))
                .Add(Restrictions.Eq(filterPropertyName, filterPropertyValue))
                .List<T>();
        }
        #endregion

        public T Get<T>(string filterPropertyName, object filterPropertyValue)
        {
            return _activeSession
                .CreateCriteria(typeof(T))
                .Add(Restrictions.Eq(filterPropertyName, filterPropertyValue))
                .UniqueResult<T>();
        }

        public IList<T> GetAll<T>()
        {
            if (_activeSession == null || (_activeSession != null && !_activeSession.IsOpen))
                this.OpenSession();
            ICriteria crit = _activeSession.CreateCriteria(typeof(T));
            
            return crit.List<T>();  

        }
        #endregion

        public void Dispose()
        {
            FlushSession();
            CloseSession();
            _activeSession = null;
        }

        public ICriteria CreateCriteria<T>() where T : class
        {
            return Session.CreateCriteria<T>();
        }
    }
}
